package com.ihai.spring.boot.sample.distributed.counter.handler;

import com.ihai.spring.boot.sample.distributed.counter.model.CounterMBean;
import com.ihai.spring.boot.sample.distributed.counter.model.ICounter;
import com.ihai.spring.boot.sample.distributed.counter.remote.NodeManager;
import com.ihai.spring.boot.sample.distributed.counter.util.JmxRemoteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * DESC
 *
 * @author 张驰
 * @date 2019/12/24
 */
public class CallMasterInvoker {
    private static final Logger logger = LoggerFactory.getLogger(CallMasterInvoker.class);
    private static ConcurrentMap<String, CounterMBean> masterCounterMBeanMap = new ConcurrentHashMap<>();
    private static ThreadLocal<AtomicInteger> failedTimes = new ThreadLocal<>();

    public static Object invoke(ICounter counter, Method method, Object[] args) {
        try {
            if(NodeManager.getMaster() != null){
                if(!masterCounterMBeanMap.containsKey(counter.getName()) || failedTimes.get() != null){
                    masterCounterMBeanMap.put(counter.getName(),JmxRemoteUtil.getMBean(NodeManager.getMaster(), "iHaiCounter","iHaiCounter:name="+counter.getName(), CounterMBean.class));
                }
                CounterMBean counterMBean = masterCounterMBeanMap.get(counter.getName());
                if(counterMBean != null){
                    counterMBean.isMaster(true);
                    return method.invoke(counterMBean, args);
                }
            }
            return method.invoke(counter, args);
        } catch (Exception e) {
            if(failedTimes.get() == null){
                failedTimes.set(new AtomicInteger(1));
            }else{
                if(failedTimes.get().incrementAndGet() == 5){
                    NodeManager.removeCluster(NodeManager.getMaster());
                    NodeManager.resetMaster();
                    masterCounterMBeanMap.clear();
                }
            }
            logger.error("call master failed.", e);
            try {
                return method.invoke(counter, args);
            }catch (Exception e1){
                logger.error("call master failed.", e1);
            }
        }
        return null;
    }

}
